package com.nmgolden.lanxin.api;

import com.alibaba.fastjson.JSONArray;
import com.alibaba.fastjson.JSONObject;
import com.alibaba.fastjson.TypeReference;
import com.nmgolden.lanxin.LanxinHttpClient;
import com.nmgolden.lanxin.LanxinAppConfig;
import com.nmgolden.lanxin.LanxinTokenManager;
import com.nmgolden.lanxin.model.*;
import lombok.SneakyThrows;

import javax.crypto.Mac;
import javax.crypto.spec.SecretKeySpec;
import java.nio.charset.StandardCharsets;
import java.util.*;

/**
 * 提供与蓝信 api 对应的接口调用方法
 *
 * @author siger
 * @date 2021/1/2 10:37 上午 Wechat:Z2013111158
 */

public class NotificationApi extends AbsApi {

    public NotificationApi(LanxinAppConfig config, LanxinHttpClient lanxinHttpClient, LanxinTokenManager lanxinTokenManager) {
        super(config, lanxinHttpClient, lanxinTokenManager);
    }


    /**
     * 发送应用消息
     *
     * @param userIds       人员 id 列表
     * @param departmentIds 部门 id 列表
     * @param data          消息体
     */
    public MsgResponseData sendMessage(List<String> userIds, List<String> departmentIds, MsgData data) {
        if ((userIds == null || userIds.isEmpty()) && (departmentIds == null || departmentIds.isEmpty())) {
            throw new IllegalArgumentException("userIds departmentIds 不可同时为空！");
        }

        MsgPayload payload = new MsgPayload();
        if (userIds != null && !userIds.isEmpty()) {
            payload.setUserIdList(userIds);
        }
        if (departmentIds != null && !departmentIds.isEmpty()) {
            payload.setDepartmentIdList(departmentIds);
        }
        payload.setMsgType(data.getMsgDataInfo().getMsgType());
        payload.setMsgData(data);

        return sendMessage(payload);
    }

    /**
     * 发送应用消息
     *
     * @param payload 消息负载
     */
    private MsgResponseData sendMessage(MsgPayload payload) {
        String SEND_APP_MESSAGE_URL_TEMPLATE = "%s/v1/messages/create?app_token=%s";
        String url = String.format(SEND_APP_MESSAGE_URL_TEMPLATE, mLanxinAppConfig.getApiGateway(), mLanxinTokenManager.getAppToken());
        Map<String, ?> requestBody = JSONObject.parseObject(JSONObject.toJSONString(payload), new TypeReference<Map<String, Object>>() {
        });
        JSONObject responseJson = mLanxinLanxinHttpClient.post(url, requestBody);
        return responseJson.toJavaObject(MsgResponseData.class);
    }

    public void updateDynamicCardMsg(String msgId, MsgData msgData) {
        String SEND_UPDATE_APP_MESSAGE_URL_TEMPLATE = "%s/v1/messages/dynamic/update?app_token=%s";
        String url = String.format(SEND_UPDATE_APP_MESSAGE_URL_TEMPLATE, mLanxinAppConfig.getApiGateway(), mLanxinTokenManager.getAppToken());
        Map<String, Object> payload = new HashMap<>();
        payload.put("msgId", msgId);
        payload.put("msgType", msgData.getMsgDataInfo().getMsgType());
        payload.put("msgData", msgData);
        Map<String, ?> requestBody = JSONObject.parseObject(JSONObject.toJSONString(payload), new TypeReference<Map<String, Object>>() {
        });
        mLanxinLanxinHttpClient.post(url, requestBody);
    }

    /**
     * 发送摘要消息
     * 接口说明： 通过该接口，应用可以给指定人发送通知消息。 特别说明：该消息类型仅展示会话列表摘要，不展示会话消息详情，点击会话摘要直接跳转应用首页入口。
     * 仅适用于通知，邮件等特定待办数量&摘要类型的消息场景。普通办公类卡片消息不建议使用该接口。
     * <a href="https://apigw-example.domain/v1/messages/notification/create?app_token=APP_TOKEN">...</a>
     * {
     * "uuid":"154fdsfa...",
     * "senderId":"524288-AB45MMKHIM",
     * "receiverIds":["524288-AB4DDDABBKHIM","524288-JIKADLJFIHENAJ",...],
     * "outline":"[通知]：关于...的通知"
     * "msgType":"msgType",
     * "msgData": {
     * "type": {}
     * }
     * }
     */
    public void sendNotification(String senderId, List<String> receiverIds, String outline, String msgType, MsgData data) {
        String SEND_APP_NOTIFICATION_URL_TEMPLATE = "%s/v1/messages/notification/create?app_token=%s";
        String url = String.format(SEND_APP_NOTIFICATION_URL_TEMPLATE, mLanxinAppConfig.getApiGateway(), mLanxinTokenManager.getAppToken());
        Map<String, Object> payload = new HashMap<>();
        payload.put("uuid", UUID.randomUUID());
        payload.put("senderId", senderId);
        payload.put("receiverIds", receiverIds);
        payload.put("outline", outline);
        payload.put("msgType", msgType);
        payload.put("msgData", data);

        mLanxinLanxinHttpClient.post(url, payload);
    }

    /**
     * 更新通知消息会话状态（批量）
     * <a href="https://apigw-example.domain/v2/chat/notification/update?app_token=APP_TOKEN">...</a>
     * 文档地址 ： <a href="https://openapi.lanxin.cn/doc/#/server-api/message/update_notification_chat_batch">...</a>
     *
     * @param userId      用户id
     * @param unreadCount 未读数量
     * @param noDisturb   是否免打扰（和unreadCount不可共存）
     * @param baseVersion 版本号
     */
    public void updateNotificationStatus(String userId, Integer unreadCount, Boolean noDisturb, long baseVersion) {

        String UPDATE_APP_NOTIFICATION_URL_TEMPLATE = "%s/v2/chat/notification/update?app_token=%s";
        String url = String.format(UPDATE_APP_NOTIFICATION_URL_TEMPLATE, mLanxinAppConfig.getApiGateway(), mLanxinTokenManager.getAppToken());
        Map<String, Object> payload = new HashMap<>();
        payload.put("uuid", UUID.randomUUID());
        payload.put("userId", userId);
        if (unreadCount != null) {
            payload.put("unreadCount", unreadCount);
        }
        if (noDisturb != null) {
            payload.put("noDisturb", String.valueOf(noDisturb));
        }
        payload.put("baseVersion", String.valueOf(baseVersion));

        Map<String, Object> data = new HashMap<String, Object>() {{
            put("statusList", Collections.singletonList(payload));
        }};
        mLanxinLanxinHttpClient.post(url, data);
    }

    public JSONObject getNotificationStatus(String userId) {
        String TPL = "%s/v1/chat/notification/%s/fetch?app_token=%s";
        String url = String.format(TPL, mLanxinAppConfig.getApiGateway(), userId, mLanxinTokenManager.getAppToken());
        return mLanxinLanxinHttpClient.get(url);
    }

    /**
     * 撤回消息
     * <a href="https://openapi.lanxin.cn/doc/#/server-api/message/revoke_message">...</a>
     * 请求数据示例
     * {
     * "chatType":"TYPE",
     * "senderId":"524288-AB4DDDABBKHIM",
     * "messageIds":["524288-JKJLDJIENVB", "524288-JKJLDJIENVB",....],
     * "sysMsg":
     * {
     * "content":"该消息已撤回",
     * "mediaId":"524288-FJEIMMDFSLFWELFWSDG"
     * }
     * }
     */
    public void revokeMessage(String chatType, String senderId, List<String> messageIds, String content, String mediaId) {
        String REVOKE_MESSAGE_URL_TEMPLATE = "%s/v1/messages/revoke?app_token=%s";
        String url = String.format(REVOKE_MESSAGE_URL_TEMPLATE, mLanxinAppConfig.getApiGateway(), mLanxinTokenManager.getAppToken());
        Map<String, Object> requestBody = new HashMap<String, Object>() {{
            put("chatType", chatType);
            put("senderId", senderId);
            put("messageIds", messageIds);

            Map<String, String> sysMsg = new HashMap<String, String>() {{
                put("content", content);
                put("mediaId", mediaId);
            }};
            put("sysMsg", sysMsg);
        }};
        mLanxinLanxinHttpClient.post(url, requestBody);
    }

    public List<SendEventNotificationSubStatusItem> sendEventNotifications(List<AppEventNotification> events) {
        String url = String.format("%s/v2/event/notification/create?app_token=%s", mLanxinAppConfig.getApiGateway(), mLanxinTokenManager.getAppToken());
        HashMap<String, Object> data = new HashMap<String, Object>() {{
            put("events", events);
        }};
        JSONObject responseJson = mLanxinLanxinHttpClient.post(url, data);

        JSONArray subStatusJsonArray = responseJson.getJSONArray("subStatus");
        List<SendEventNotificationSubStatusItem> subStatusList = new ArrayList<>();
        for(int i =-1; ++i< subStatusJsonArray.size();){
            JSONObject item = subStatusJsonArray.getJSONObject(i);
            SendEventNotificationSubStatusItem sendEventNotificationSubStatusItem = new SendEventNotificationSubStatusItem();
            sendEventNotificationSubStatusItem.setErrCode(item.getInteger("errCode"));
            sendEventNotificationSubStatusItem.setErrMsg(item.getString("errMsg"));
            subStatusList.add(sendEventNotificationSubStatusItem);
        }
        return subStatusList;
    }

    public BotMsgResponseData sendMsgByBot(String url, String secret, MsgDataInfo msgDataInfo) {
        Map<String, Object> requestBody = new HashMap<>();
        requestBody.put("msgType", msgDataInfo.getMsgType());
        requestBody.put("msgData", createMsgData(msgDataInfo));
        if (secret != null) {
            long timestamp = System.currentTimeMillis() / 1000;
            String sign = createBotMsgSign(secret, timestamp);
            requestBody.put("timestamp", String.valueOf(timestamp));
            requestBody.put("sign", sign);
        }
        JSONObject responseJson = mLanxinLanxinHttpClient.post(url, requestBody);

        return responseJson.toJavaObject(BotMsgResponseData.class);
    }

    private MsgData createMsgData(MsgDataInfo msgDataInfo) {
        if (msgDataInfo instanceof AppCardMsgDataInfo) {
            return new AppCardMsgData((AppCardMsgDataInfo) msgDataInfo);
        }
        if (msgDataInfo instanceof OACardMsgDataInfo) {
            return new OACardMsgData((OACardMsgDataInfo) msgDataInfo);
        }

        if (msgDataInfo instanceof LinkCardMsgDataInfo) {
            return new LinkCardMsgData((LinkCardMsgDataInfo) msgDataInfo);
        }
        if (msgDataInfo instanceof TextMsgDataInfo) {
            return new TextMsgData((TextMsgDataInfo) msgDataInfo);
        }

        throw new IllegalArgumentException("不支持的 MsgDataInfo 类型！");
    }

    @SneakyThrows
    private String createBotMsgSign(String secret, long timestamp) {
        // 将时间戳和秘钥拼接成待签名字符串
        String toSign = timestamp + "@" + secret;

        Mac sha256Hmac = Mac.getInstance("HmacSHA256");
        sha256Hmac.init(new SecretKeySpec(toSign.getBytes(StandardCharsets.UTF_8), "HmacSHA256"));
        byte[] signatureBytes = sha256Hmac.doFinal(new byte[]{});

        // 对签名结果进行 Base64 编码
        return Base64.getEncoder().encodeToString(signatureBytes);
    }

}
